package com.goldgov.code.generate.impl;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.goldgov.code.generate.CodeGenerator;
import com.goldgov.code.meta.Constraint;
import com.goldgov.code.meta.Constraint.QueryType;
import com.goldgov.code.meta.FieldInfo;
import com.goldgov.code.meta.FieldType;
import com.goldgov.code.meta.ModuleInfo;
import com.goldgov.code.template.Group;
import com.goldgov.code.template.Layout;
import com.goldgov.code.template.commons.CommonPlugin;
import com.goldgov.code.template.model.CodePointModel;
import com.goldgov.code.template.model.OptionOutputModel;
import com.goldgov.code.template.point.CodePoint;
import com.goldgov.code.template.point.PointMapping;
import com.goldgov.code.utils.FreemarkerUtils;
import com.goldgov.code.utils.PackageUtils;
import com.goldgov.code.utils.PlaceholderUtils;

import freemarker.template.Template;
import freemarker.template.TemplateException;

public class FreemarkerGeneratorImpl implements CodeGenerator{

	private CommonPlugin[] plugins;
	
	@Override
	public void generate(String packagePath,ModuleInfo[] moduleInfos,Group group,File outputBaseDir,Map<String, Object> paramMap) throws Exception {
		Layout[] layouts = group.getLayouts();
		File basePath = group.getBasePath();
		FreemarkerUtils.init(basePath);
		if(layouts != null) {
			outputBaseDir = new File(outputBaseDir,PackageUtils.convertPath(packagePath));
			if(!outputBaseDir.exists()) {
				outputBaseDir.mkdirs();
			}
			for (ModuleInfo moduleInfo : moduleInfos) {
				for (Layout layout : layouts) {
					layout.init(moduleInfo);
					String templatePath = layout.templatePath();
					templatePath = templatePath == null ? "" : templatePath;
					File templateDir = new File(basePath , templatePath);
					
					String packageName = layout.packageName();
					if(packageName == null) {
						packageName = templatePath;
					}
					File outputDir = new File(outputBaseDir , moduleInfo.getPackageName() + File.separator + packageName);
					HashMap<String, Object> dataMap = new HashMap<>();
					dataMap.put("module", moduleInfo);
					dataMap.put("package", packagePath);
					dataMap.put("author",group.getAuthor());
					dataMap.put("extParam",paramMap);
					
					
					PointMapping pointMapping = layout.codePoint(packagePath,moduleInfo, group,dataMap);
					if(pointMapping == null) {
						pointMapping = new PointMapping();
					}
					
					String[] pluginNames = null;
					if(plugins != null) {
						pluginNames = new String[plugins.length];
						int index = 0;
						for (CommonPlugin plugin : plugins) {
							if(plugin.supports(moduleInfo, layouts)) {
								PointMapping pluginPointMapping = plugin.doLayout(moduleInfo, layouts,dataMap);
								if(pluginPointMapping != null) {
									pointMapping.merge(pluginPointMapping);
								}
							}
							pluginNames[index++] = plugin.name();
						}
					}
					dataMap.put("plugins", pluginNames);
					
					generateFiles(templateDir,outputDir,dataMap,templatePath,pointMapping);
				}
			}
		}
	}
	
	
	private void generateFiles(File templateDir,File outputDir,Map<String,Object> dataMap,String templatePath, PointMapping pointMapping) throws Exception {
		if(!outputDir.exists()) {
			outputDir.mkdirs();
		}
		File[] fileLists = templateDir.listFiles();
		if(fileLists == null){
			throw new NullPointerException(templateDir.getAbsolutePath());
		}
		for (int i = 0; i < fileLists.length; i++) {
			if(fileLists[i].isDirectory()){
				File oPath = new File(outputDir,fileLists[i].getName());
				oPath.mkdirs();
				generateFiles(fileLists[i],oPath,dataMap,templatePath + "/" + fileLists[i].getName(),pointMapping);
			}else{
				try {
					String templateName = templatePath + "/" + fileLists[i].getName();
					Template template = FreemarkerUtils.getTemplate(templateName);
					String outName = fileLists[i].getName();
					outName = outName.substring(0, outName.length()-4);//-4 为“.ftl”
					
					outName = formatFileName(outName,(ModuleInfo) dataMap.get("module"));
					
					List<CodePoint> mappingPoint = pointMapping.getMappingPoint(templateName);
					dataMap.put("point", new CodePointModel(mappingPoint));
					dataMap.put("opt", new OptionOutputModel());
					
					OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream(outputDir + File.separator + outName),"UTF-8");
					template.process(dataMap, outputStreamWriter);
					outputStreamWriter.close();
				} catch (IOException e1) {
					throw e1;
				} catch (TemplateException e) {
					throw e;
				}
			}
		}
	}
	
	private String formatFileName(String originName,ModuleInfo moduleInfo) {
		Map<String,Object> dataMap = new HashMap<>();
		dataMap.put("name",moduleInfo.getName());
		return PlaceholderUtils.replace(originName, dataMap);
	}

//	public static void main(String[] args) throws Exception {
//		FreemarkerGeneratorImpl generator = new FreemarkerGeneratorImpl();
//		Group group = new Group();
//		group.setBasePath(new File("D:\\Git\\generate-code\\template"));
//		BeanLayout beanLayout = new BeanLayout();
//		DaoLayout daoLayout = new DaoLayout();
//		ServiceLayout serviceLayout = new ServiceLayout();
//		WebLayout webLayout = new WebLayout();
//		PageLayout pageLayout = new PageLayout();
//		group.setLayouts(new Layout[] {beanLayout,daoLayout,serviceLayout,webLayout,pageLayout});
//		
//		ModuleInfo moduleInfo = new ModuleInfo();
//		moduleInfo.setTableName("USER_TABLE");
//		moduleInfo.setName("User");
//		moduleInfo.setExplain("用户表");
//		
//		FieldInfo[] fields = new FieldInfo[5];
//		fields[0] = new FieldInfo();
//		fields[0].setField("LOGIN_NAME");
//		fields[0].setExplain("登录名");
//		fields[0].setName("loginName");
//		Constraint constraint = new Constraint(QueryType.LIKE);
//		fields[0].setConstraint(constraint);
//		fields[0].setType(FieldType.VARCHAR);
//		
//		fields[1] = new FieldInfo();
//		fields[1].setField("BIRTHDAY");
//		fields[1].setName("birthday");
//		fields[1].setType(FieldType.DATE);
//		
//		fields[2] = new FieldInfo();
//		fields[2].setField("USER_ID");
//		fields[2].setExplain("用户ID");
//		fields[2].setName("userID");
//		fields[2].setType(FieldType.VARCHAR);
//		constraint = new Constraint();
//		constraint.setPrimaryKey(true);
//		fields[2].setConstraint(constraint);
//		
//		fields[3] = new FieldInfo();
//		fields[3].setField("PARENT_ID");
//		fields[3].setName("parentID");
//		fields[3].setType(FieldType.VARCHAR);
//
//		fields[4] = new FieldInfo();
//		fields[4].setField("TREE_PATH");
//		fields[4].setName("treePath");
//		fields[4].setType(FieldType.VARCHAR);
//		
//		moduleInfo.setFields(fields);
//		
//		ModuleInfo orgModule = orgModule();
//		
//		RelationInfo relation = new RelationInfo(orgModule,RelationType.MANY_TO_ONE);
//		relation.setRelationField("ORG_ID");
//		moduleInfo.setRelation(relation);
//		
//		generator.setPlugins(new CommonPlugin[] {new TreePlugin()});
//		group.setAuthor(new AuthorInfo("1","Zhang3"));
//		generator.generate("com.xxxx",new ModuleInfo[] {moduleInfo,orgModule}, group, new File("E:\\codes"),Collections.EMPTY_MAP);
//		
////		SqlMetaSource sqlMetaSource = new SqlMetaSource(new File("D:\\sql3.sql"));
////		ModuleInfo[] resolve = sqlMetaSource.resolve();
////		generator.generate("com.goldgov.code",resolve, group, new File("D:\\codes"));
//		
//	}
	
	private static ModuleInfo orgModule() {
		ModuleInfo moduleInfo = new ModuleInfo();
		
		moduleInfo.setTableName("ORG_TABLE");
		moduleInfo.setName("Organization");
		moduleInfo.setExplain("组织机构表");
		
		FieldInfo[] fields = new FieldInfo[2];
		fields[0] = new FieldInfo();
		fields[0].setField("ORG_NAME");
		fields[0].setExplain("机构名");
		fields[0].setName("orgName");
		Constraint constraint = new Constraint(QueryType.LIKE);
		fields[0].setConstraint(constraint);
		fields[0].setType(FieldType.VARCHAR);
		
		fields[1] = new FieldInfo();
		fields[1].setField("ORG_ID");
		fields[1].setExplain("机构ID");
		fields[1].setName("orgID");
		fields[1].setType(FieldType.VARCHAR);
		constraint = new Constraint();
		constraint.setPrimaryKey(true);
		fields[1].setConstraint(constraint);
		
		moduleInfo.setFields(fields);

		return moduleInfo;
	}


	@Override
	public void setPlugins(CommonPlugin[] plugins) {
		this.plugins = plugins;
	}
	
}
